import { Injector } from '@angular/core';
import { IControlService } from '@farris/designer-element';
import {
    DesignerEnvType, DesignerHostSettingService, DgControl, DomService, FormBasicService, FormBindingType,
    FormComponentType, FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName, FormViewModel, WebCmdService
} from '@farris/designer-services';
import { cloneDeep } from 'lodash-es';
import { ComponentBuildInfo } from './inner-component-build-info';
const ROOT_VIEW_MODEL_ID = 'root-viewmodel';

export class ComponentCreatorService {

    public controlCreatorService: any;
    public controlService: IControlService;
    public formBasicService: FormBasicService;

    public webCmdService: WebCmdService;

    public injector: Injector;
    constructor(
        private designerHostSettingServ: DesignerHostSettingService,
        private domService: DomService, injector?: Injector) {
        if (this.designerHostSettingServ) {
            this.controlService = this.designerHostSettingServ.controlService;
            this.controlCreatorService = this.designerHostSettingServ.controlCreatorService;
            this.injector = this.designerHostSettingServ.designerHost.getService('Injector') as Injector;
            this.formBasicService = this.injector.get(FormBasicService);

            this.webCmdService = this.injector.get(WebCmdService);

        }

        if (injector) {
            this.webCmdService = injector.get(WebCmdService);
            this.injector = injector;
        }
    }

    createComponentRefNode(buildInfo: ComponentBuildInfo): any {
        const componentRefNode = this.controlService.getControlMetaData(DgControl.ComponentRef.type);
        Object.assign(componentRefNode, {
            id: `${buildInfo.componentIdPrefix}-ref`,
            component: buildInfo.componentIdPrefix
        });
        return componentRefNode;
    }

    createComponentNode(buildInfo: ComponentBuildInfo): any {
        const componentNode = this.controlService.getControlMetaData(DgControl.Component.type);
        const contents = this.createComponentContents(buildInfo);
        Object.assign(componentNode, {
            id: `${buildInfo.componentIdPrefix}`,
            viewModel: `${buildInfo.componentIdPrefix}-viewmodel`,
            componentType: buildInfo.resolvedComponentType,
            appearance: {
                // class: buildInfo.componentAppearance
                class: this.getComponentAppearance(buildInfo.resolvedComponentType)
            },
            contents
        });
        return componentNode;
    }

    getComponentAppearance(componentType: string) {
        switch (componentType) {
            case FormComponentType.form: case FormComponentType.table: {
                return this.getFormComponentClass();
            }
            case FormComponentType.dataGrid: {
                return this.getDataGridComponentClass();
            }
            case 'AttachmentPanel': case FormComponentType.attachmentPanel: {
                return this.getAttachmenetComponentClass();
            }
            case FormComponentType.appointmentCalendar: {
                return 'f-struct-wrapper f-utils-fill-flex-column';
            }
            default: {
                if (componentType.startsWith('form-col-')) {
                    return this.getFormComponentClass();
                }
                return '';
            }
        }
    }
    /**
     * 获取卡片组件层级的class样式
     */
    private getFormComponentClass(): string {
        const templateId = this.domService.module.templateId;

        // 双列表标签页模板中拖入卡片
        if (templateId === 'double-list-in-tab-template') {
            return 'f-struct-wrapper f-utils-fill-flex-column';
        }
        return 'f-struct-wrapper';

    }
    /**
     * 获取列表组件层级的class样式
     */
    private getDataGridComponentClass(): string {
        const templateId = this.domService.module.templateId;

        // 双列表标签页模板，要求列表填充表单高度
        if (templateId === 'double-list-in-tab-template') {
            return 'f-struct-wrapper f-utils-fill-flex-column';
        }
        return 'f-struct-is-subgrid';

    }

    /**
     * 获取列表组件层级的class样式
     */
    private getAttachmenetComponentClass(): string {
        const templateId = this.domService.module.templateId;

        // 双列表标签页模板，要求列表填充表单高度
        if (templateId === 'double-list-in-tab-template') {
            return 'f-struct-wrapper f-utils-fill-flex-column';
        }
        return 'f-struct-wrapper';
    }

    private createComponentContents(buildInfo: ComponentBuildInfo) {
        switch (buildInfo.componentType) {
            case 'form': case 'Form':
                return this.createFormComponentContents(buildInfo);
            case 'DataGrid':
                return this.createDateGridComponentContents(buildInfo);
            case 'TreeGrid':
                return this.createTreeGridComponentContents(buildInfo);
            case 'ListView':
                return this.createListViewComponentContents(buildInfo);
            case 'FileUploadPreview':
                return this.createAttachmentComponentContents(buildInfo);
            case 'AppointmentCalendar': {
                return this.createAppointmentCalendarComponentContents(buildInfo);
            }
            default: {
                if (buildInfo.componentType.startsWith('form-col-')) {
                    return this.createFormComponentContents(buildInfo);
                }
            }
        }
    }



    private createFormComponentContents(buildInfo: ComponentBuildInfo) {
        // 1、创建section
        const section = this.controlService.getControlMetaData(DgControl.Section.type);
        Object.assign(section, {
            id: buildInfo.componentId + '-form-section',
            appearance: {
                class: 'f-section-form f-section-in-mainsubcard'
            },
            mainTitle: buildInfo.componentName
        });

        // 2、创建form(默认控件标签独占一列)
        const layoutForm = this.controlService.getControlMetaData(DgControl.Form.type);
        const controls = [];
        Object.assign(layoutForm, {
            id: buildInfo.componentId + '-form-layout',
            appearance: {
                class: 'f-form-layout farris-form farris-form-controls-inline'
            },
            contents: controls
        });

        // 零代码输入控件的长度不受farris最大长度限制
        if (this.formBasicService && this.formBasicService.envType === DesignerEnvType.noCode) {
            layoutForm.appearance.class += ' farris-form-auto';
        }
        section.contents = [layoutForm];

        // 3、创建字段
        const { selectedFields } = buildInfo;
        selectedFields.forEach(field => {
            const dgVMField = cloneDeep(field);
            const fieldMetadata = this.controlCreatorService.createControlBySchemaFeild(dgVMField, buildInfo.resolvedComponentType);

            if (!fieldMetadata) {
                return;
            }
            // 默认创建的控件按照选定的布局设置样式，不支持自适应
            if (!fieldMetadata.appearance.class) {
                fieldMetadata.appearance.class = this.getFormControlClassName(buildInfo);
            }
            controls.push(fieldMetadata);
        });


        // 双列表标签页模板中拖入卡片，要求卡片要填充
        const templateId = this.domService.module.templateId;
        if (templateId === 'double-list-in-tab-template') {
            section.appearance.class = 'f-section-grid f-section-in-main px-0 pt-0';
            section.fill = true;
        }
        return [section];
    }

    private createDateGridComponentContents(buildInfo: ComponentBuildInfo) {
        const templateId = this.domService.module.templateId;
        let container;
        // 根据模板不同，创建不同的容器类型和样式
        if (templateId === 'double-list-in-tab-template') {
            // 1、创建setion
            const section = this.controlService.getControlMetaData(DgControl.Section.type);
            Object.assign(section, {
                id: buildInfo.componentIdPrefix + '-section',
                appearance: {
                    class: 'f-section-grid f-section-in-main px-0 pt-0'
                },
                fill: true,
                showHeader: false
            });
            container = section;
        } else {
            // 1、创建contentContainer
            const contentContainer = this.controlService.getControlMetaData(DgControl.ContentContainer.type);
            Object.assign(contentContainer, {
                id: buildInfo.componentIdPrefix + '-layout',
                appearance: {
                    class: 'f-grid-is-sub f-utils-flex-column'
                }
            });
            container = contentContainer;
        }


        // 2、创建DataGrid
        const dataGrid = this.controlService.getControlMetaData(DgControl.DataGrid.type);
        const fields = [];

        Object.assign(dataGrid, {
            id: buildInfo.componentId + '-dataGrid',
            appearance: {
                class: 'f-component-grid f-utils-fill'
            },
            fields,
            fieldEditable: buildInfo.editable,
            dataSource: buildInfo.dataSource
        });
        container.contents = [dataGrid];
        const { selectedFields } = buildInfo;
        // 3、创建字段
        selectedFields.forEach(field => {
            const dgVMField = cloneDeep(field);
            const fieldMetadata = this.controlCreatorService.createGridFieldBySchemaFeild(dgVMField, field.bindingPath,
                buildInfo.editable, DgControl.GridField.type, 'Farris');
            if (fieldMetadata) {
                fields.push(fieldMetadata);
            }
        });
        return [container];
    }

    private createTreeGridComponentContents(buildInfo: ComponentBuildInfo) {
        // 1、创建contentContainer
        const contentContainer = this.controlService.getControlMetaData(DgControl.ContentContainer.type);
        Object.assign(contentContainer, {
            id: buildInfo.componentIdPrefix + '-layout',
            appearance: {
                class: 'f-grid-is-sub f-utils-flex-column'  // TODO待优化
            }
        });

        // 2、创建TreeGrid
        const dataGrid = this.controlService.getControlMetaData(DgControl.TreeGrid.type);
        const fields = [];

        Object.assign(dataGrid, {
            id: buildInfo.componentId + '-treeGrid',
            appearance: {
                class: 'f-component-treetable'
            },
            fields,
            dataSource: buildInfo.dataSource
            // dataSource: this.selectedCmp.label
        });
        contentContainer.contents = [dataGrid];
        const { selectedFields } = buildInfo;
        // 3、创建字段
        selectedFields.forEach(field => {
            const dgVMField = cloneDeep(field);
            const fieldMetadata = this.controlCreatorService.createGridFieldBySchemaFeild(dgVMField, field.bindingPath,
                false, DgControl.TreeGridField.type, 'Farris');
            fields.push(fieldMetadata);
        });
        return [contentContainer];
    }

    /**
     * 预约日历组件
     * @param buildInfo 构造信息
     */
    private createAppointmentCalendarComponentContents(buildInfo: ComponentBuildInfo) {
        // 1、创建setion
        const section = this.controlService.getControlMetaData(DgControl.Section.type);
        Object.assign(section, {
            id: buildInfo.componentIdPrefix + '-section',
            appearance: {
                class: 'f-section-grid f-section-in-managelist'
            },
            fill: true,
            showHeader: false
        });

        // 2、创建日历
        const appointmentCalendar = this.controlService.getControlMetaData(DgControl.AppointmentCalendar.type);
        Object.assign(appointmentCalendar, {
            id: buildInfo.componentId + '-calendar',
            appearance: {
                class: 'f-utils-fill-flex-row f-component-appointment-calendar'
            },
            dataSource: buildInfo.dataSource,
            filterChange: `${buildInfo.componentId.replace(/-/g, '')}Load`,
            removeHandler: `${buildInfo.componentId.replace(/-/g, '')}Remove`,
            reservation: `${buildInfo.componentId.replace(/-/g, '')}OpenAppAndAdd1`
        });
        section.contents = [appointmentCalendar];

        return [section];
    }

    private createListViewComponentContents(buildInfo: ComponentBuildInfo) {
        // 1、创建ListView
        const listView = this.controlService.getControlMetaData(DgControl.ListView.type);

        Object.assign(listView, {
            id: buildInfo.componentId + '-listview',
            appearance: {
                class: ''
            },
            fill: true
        });

        return [listView];
    }
    /**
     * 创建附件组件
     * @param buildInfo 组件构造信息
     */
    private createAttachmentComponentContents(buildInfo: ComponentBuildInfo) {
        // 1、创建section
        const section = this.controlService.getControlMetaData(DgControl.Section.type);
        Object.assign(section, {
            id: buildInfo.componentId + '-section',
            appearance: {
                class: 'f-section-form f-section-in-mainsubcard'
            },
            mainTitle: buildInfo.componentName ? buildInfo.componentName : '附件',
            showHeader: buildInfo.parentContainerType === DgControl.Tab.type ? false : true,
            contents: []
        });
        // 根据模板不同，创建不同的样式
        const templateId = this.domService.module.templateId;
        if (templateId === 'double-list-in-tab-template') {
            section.appearance.class = 'f-section-grid f-section-in-main px-0 pt-0';
            section.showHeader = false;
            section.fill = true;
        }

        // 2、 创建附件
        const uploadPreview = this.controlService.getControlMetaData(DgControl.FileUploadPreview.type);

        Object.assign(uploadPreview, {
            id: buildInfo.componentId + '-file'
        });
        section.contents.push(uploadPreview);

        // 附件信息udt字段
        let attachmentUdtField;
        const selectedFields = buildInfo.selectedEntity && buildInfo.selectedEntity.type.fields;
        if (selectedFields && selectedFields.length) {
            attachmentUdtField = selectedFields.find(field => field.$type === FormSchemaEntityField$Type.ComplexField && field.type && field.type.name && field.type.name.includes('AttachmentInfo'));
        }
        if (!attachmentUdtField || !attachmentUdtField.type || !attachmentUdtField.type.fields) {
            return [section];
        }
        // 预置附件id字段和名称字段属性值
        const attachmentIdField = attachmentUdtField.type.fields.find(field => field.code === 'AttachmentId');
        const attachmentFileSortOrderField = attachmentUdtField.type.fields.find(field => field.code === 'FileSortOrder');
        const attachmentNameField = attachmentUdtField.type.fields.find(field => field.code === 'FileName');
        uploadPreview.attachmentFieldId = attachmentUdtField.label;
        if (attachmentFileSortOrderField) {
            uploadPreview.fileSortOrderKey = {
                type: FormBindingType.Form,
                path: attachmentFileSortOrderField.bindingField,
                field: attachmentFileSortOrderField.id,
                fullPath: attachmentFileSortOrderField.path,
                bindingPath: attachmentFileSortOrderField.bindingPath
            };
        }
        if (attachmentIdField) {
            uploadPreview.fieldIdKey = {
                type: FormBindingType.Form,
                path: attachmentIdField.bindingField,
                field: attachmentIdField.id,
                fullPath: attachmentIdField.path,
                bindingPath: attachmentIdField.bindingPath
            };
        }
        if (attachmentNameField) {
            uploadPreview.fileNameKey = {
                type: FormBindingType.Form,
                path: attachmentNameField.bindingField,
                field: attachmentNameField.id,
                fullPath: attachmentNameField.path,
                bindingPath: attachmentNameField.bindingPath
            };
        }


        // 预置附件上传、删除命令
        const prefix = buildInfo.componentId.replace('fileuploadpreview', '').replace(/-/g, '').replace(/_/g, '');
        uploadPreview.fUploadDoneEvent = `${prefix}AddFileRows`;
        uploadPreview.fileRemoveEvent = `${prefix}RemoveFileRows`;

        return [section];

    }
    createViewModeNode(buildInfo: ComponentBuildInfo): any {
        // 3、 添加viewModel节点
        const viewModelNode = new FormViewModel();
        Object.assign(viewModelNode, {
            id: `${buildInfo.componentIdPrefix}-viewmodel`,
            code: `${buildInfo.componentIdPrefix}-viewmodel`,
            name: buildInfo.componentName,
            bindTo: buildInfo.bindTo,
            parent: ROOT_VIEW_MODEL_ID,
            fields: this.assembleViewModelFields(buildInfo),
            commands: [],
            states: [],
            enableValidation: true,
            pagination: {
                enable: false
            }
        });

        if (buildInfo.componentType === 'AppointmentCalendar') {
            this.assembleAppointmentCalendarCommands(viewModelNode, buildInfo);
        }
        if (buildInfo.componentType === 'FileUploadPreview') {
            this.assembleAttachementCommands(viewModelNode, buildInfo);
        }
        return viewModelNode;
    }

    /**
     * 组装viewModel fields 节点
     */
    private assembleViewModelFields(buildInfo: ComponentBuildInfo) {

        if (buildInfo.componentType === 'FileUploadPreview') {
            return [];
        }
        const vmFields = [];
        const { selectedFields } = buildInfo;
        selectedFields.forEach(field => {
            let updateOn = 'blur';
            const type = field.type.name;
            if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) {
                updateOn = 'change';
            }

            vmFields.push({
                type: FormBindingType.Form,
                id: field.id,
                fieldName: field.bindingField,
                groupId: null,
                groupName: null,
                updateOn,
                fieldSchema: {}
            });
        });
        return vmFields;
    }

    /**
     * 默认创建的控件按照选定的布局设置样式，不支持自适应
     */
    private getFormControlClassName(buildInfo: ComponentBuildInfo) {
        switch (buildInfo.formColumns) {
            case 1: {
                return 'col-12';
            }
            case 2: {
                return 'col-6';
            }
            case 3: {
                return 'col-4';
            }
            case 4: {
                return 'col-3';
            }
            default: {
                return '';
            }
        }
    }

    /**
     * 添加附件上传、删除命令
     * @param viewModel 视图模型
     * @param buildInfo 组件构造上下文
     */
    private assembleAttachementCommands(viewModel: FormViewModel, buildInfo: ComponentBuildInfo) {
        const prefix = buildInfo.componentId.replace('fileuploadpreview', '').replace(/-/g, '').replace(/_/g, '');
        const addCommandCode = `${prefix}AddFileRows`;
        const removeCommandCode = `${prefix}RemoveFileRows`;

        let attachmentUdtField;
        const attachCmpId = '31c1022c-ab40-4e8d-bc31-85d539f1d36c';
        const selectedFields = buildInfo.selectedEntity && buildInfo.selectedEntity.type.fields;
        if (selectedFields && selectedFields.length) {
            attachmentUdtField = selectedFields.find(field => field.$type === FormSchemaEntityField$Type.ComplexField && field.type && field.type.name && field.type.name.includes('AttachmentInfo'));
        }
        if (!attachmentUdtField) {
            return;
        }
        const attachCommands = [
            {
                id: `${addCommandCode}-id`,
                code: addCommandCode,
                name: '批量添加文件数据',
                handlerName: 'addFileRows',
                cmpId: attachCmpId,
                params: [
                    {
                        name: 'fileInfoFieldPath',
                        shownName: '文件信息字段路径',
                        value: `${buildInfo.bindTo}/${attachmentUdtField.label}`
                    }
                ],
                shortcut: {},
                extensions: [],
                isInvalid: false
            },
            {
                id: `${removeCommandCode}-id`,
                code: removeCommandCode,
                name: '批量删除文件数据',
                handlerName: 'removeFileRows',
                cmpId: attachCmpId,
                params: [
                    {
                        name: 'fileInfoFieldPath',
                        shownName: '文件信息字段路径',
                        value: `${buildInfo.bindTo}/${attachmentUdtField.label}`
                    }
                ],
                shortcut: {},
                extensions: [],
                isInvalid: false
            }
        ];

        viewModel.commands = viewModel.commands.concat(attachCommands);

        // 在命令构件的引用信息中记录子表的命令对构件方法的引用。
        const webcmds = this.domService.getWebCmds();
        let attachcmd = webcmds.find(item => item.id === attachCmpId);
        if (!attachcmd) {
            attachcmd = {
                id: attachCmpId,
                path: 'Gsp/Web/AttachmentCmp/bo-attachmentcmp/metadata/webcmd',
                name: 'FileController.webcmd',
                refedHandlers: []
            };
            webcmds.push(attachcmd);

            // 涉及到新增控制器，所以这里需要获取控制器信息，方便交互面板展示命令数据。
            // if (this.formBasicService.envType === DesignerEnvType.designer) {
            //     // 低代码环境要先安装附件包的依赖
            //     const metadataService = this.injector.get(GSPMetadataService);
            //     if (metadataService && metadataService.addDependencyAndRestore) {
            //         metadataService.addDependencyAndRestore(this.formBasicService.formMetaBasicInfo.relativePath, 'Inspur.GS.Gsp.Web.AttachmentCmp', true).subscribe(data => {
            //             if (this.webCmdService) {
            //                 this.webCmdService.checkCommands();
            //             }
            //         });
            //     }
            // } else {
            if (this.webCmdService) {
                this.webCmdService.checkCommands();
            }
            // }
        }

        for (const command of attachCommands) {
            attachcmd.refedHandlers.push({ host: command.id, handler: command.handlerName });
        }
    }

    /**
     * 添加预约日历相关的变量、命令
     * @param viewModel 视图模型
     * @param buildInfo 组件构造上下文
     */
    private assembleAppointmentCalendarCommands(viewModel: FormViewModel, buildInfo: ComponentBuildInfo) {
        //  预置变量
        viewModel.states.push(
            {
                id: `${buildInfo.componentId}-startDate`,
                code: 'startDate',
                name: '日历视图开始时间',
                type: 'String',
                category: 'locale'
            },
            {
                id: `${buildInfo.componentId}-endDate`,
                code: 'endDate',
                name: '日历视图结束时间',
                type: 'String',
                category: 'locale'
            },
            {
                id: `${buildInfo.componentId}-viewType`,
                code: 'viewType',
                name: '日历视图类型',
                type: 'String',
                category: 'locale'
            });

        // 预置命令
        const listControllerId = '70b4abd4-9f2c-4b7c-90e9-6ac6f4b74c72';
        const calendarCommands = [
            {
                id: `${buildInfo.componentId.replace(/-/g, '')}-load`,
                code: `${buildInfo.componentId.replace(/-/g, '')}Load`,
                name: '加载数据1',
                handlerName: 'Load',
                cmpId: listControllerId,
                params: [],
                shortcut: {},
                extensions: []
            },
            {
                id: `${buildInfo.componentId.replace(/-/g, '')}-remove`,
                code: `${buildInfo.componentId.replace(/-/g, '')}Remove`,
                name: '删除当前数据1',
                handlerName: 'Remove',
                cmpId: listControllerId,
                params: [
                    {
                        name: 'id',
                        shownName: '待删除数据的标识',
                        value: `{DATA~/#{${buildInfo.componentIdPrefix}}/id}`
                    }
                ],
                shortcut: {},
                extensions: []
            },
            {
                id: `${buildInfo.componentId.replace(/-/g, '')}-openAppAndAdd1`,
                code: `${buildInfo.componentId.replace(/-/g, '')}OpenAppAndAdd1`,
                name: '在新页签中打开应用页面新增数据1',
                params: [{
                    name: 'appId',
                    shownName: '应用标识',
                    value: ''
                },
                {
                    name: 'appEntrance',
                    shownName: '应用入口',
                    value: ''
                },
                {
                    name: 'params',
                    shownName: '附加参数',
                    value: ''
                },
                {
                    name: 'tabName',
                    shownName: '标签页标题',
                    value: ''
                },
                {
                    name: 'enableRefresh',
                    shownName: '支持刷新列表数据',
                    value: ''
                },
                {
                    name: 'destructuring',
                    shownName: '是否解构参数',
                    value: ''
                }],
                handlerName: 'OpenAppAndAdd',
                cmpId: listControllerId,
                shortcut: {},
                extensions: []
            }
        ];
        viewModel.commands = viewModel.commands.concat(calendarCommands);

        // 在命令构件的引用信息中记录新增方法。
        const webcmds = this.domService.getWebCmds();
        let listcmd = webcmds.find(item => item.id === listControllerId);
        if (!listcmd) {
            listcmd = {
                id: listControllerId,
                path: 'Gsp/Web/webcmp/bo-webcmp/metadata/webcmd',
                code: 'ListController',
                name: 'ListController.webcmd',
                refedHandlers: []
            };
            webcmds.push(listcmd);

            // 涉及到新增控制器，所以这里需要获取控制器信息，方便交互面板展示命令数据
            if (this.webCmdService) {
                this.webCmdService.checkCommands();
            }
        }

        for (const command of calendarCommands) {
            listcmd.refedHandlers.push({ host: command.id, handler: command.handlerName });
        }
    }
}
